//! 这个是用来处理Options请求的，返回验证的response。

use crate::middleware::{pin_box_future, MiddleError, PinBoxFuture};
use axum::{
    http::{header, HeaderValue, Request, StatusCode},
    response::{IntoResponse, Response},
    BoxError,
};
use tower::{Layer, Service};

#[derive(Debug, Clone)]
pub struct OptionRequestLayer;

impl OptionRequestLayer {
    pub fn new() -> Self {
        OptionRequestLayer
    }
}

impl<S> Layer<S> for OptionRequestLayer {
    type Service = OptionRequest<S>;

    fn layer(&self, inner: S) -> Self::Service {
        OptionRequest { inner }
    }
}

#[derive(Debug, Clone)]
pub struct OptionRequest<S> {
    inner: S,
}

impl<S, B> Service<Request<B>> for OptionRequest<S>
where
    S: Service<Request<B>>,
    S::Error: Into<BoxError>,
    S::Future: Send + 'static,
    S::Response: IntoResponse,
    B: Send + 'static,
{
    type Response = Response;
    type Error = BoxError;
    type Future = PinBoxFuture<Result<Self::Response, Self::Error>>;

    fn poll_ready(
        &mut self,
        cx: &mut std::task::Context<'_>,
    ) -> std::task::Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx).map_err(Into::into)
    }

    fn call(&mut self, req: Request<B>) -> Self::Future {
        let method = req.method().clone();
        if method.to_string().as_str() != "OPTIONS" {
            let req = self.inner.call(req);
            return pin_box_future(async move {
                return req
                    .await
                    .map_err(Into::into)
                    .map(IntoResponse::into_response);
            });
        }

        let headers = req.headers().clone();
        let access_control_request_method =
            headers.get(header::ACCESS_CONTROL_REQUEST_METHOD).cloned();
        let access_control_request_headers =
            headers.get(header::ACCESS_CONTROL_REQUEST_HEADERS).cloned();
        let origin = headers.get(header::ORIGIN).cloned();
        pin_box_future(async move {
            if access_control_request_headers.is_none()
                || access_control_request_method.is_none()
                || origin.is_none()
            {
                tracing::error!("request headers or method or origin 丢失");
                return Err(
                    MiddleError(StatusCode::BAD_REQUEST, "错误的options请求".to_string()).into(),
                );
            }
            let acr_headers = access_control_request_headers.unwrap();
            let acr_method = access_control_request_method.unwrap();
            let origin = origin.unwrap();
            // 直接响应res
            let mut res = Response::default();
            let header_mut = res.headers_mut();
            let _ = header_mut.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin);
            let _ = header_mut.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, acr_headers);
            let _ = header_mut.insert(header::ACCESS_CONTROL_ALLOW_METHODS, acr_method);
            let _ = header_mut.insert(
                header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
                HeaderValue::from_static("true"),
            );
            Ok(res)
        })
    }
}
